Media Stack
← Home
Services running on PCT 102 (media-core, 192.168.2.191). Stack: media-core (compose 26).
Service URLs
| Service | URL | Port |
|---|---|---|
| Jellyfin | jellyfin.carr-family.org |
8096 (standalone, static route) |
| Sonarr | sonarr.carr-family.org |
8989 |
| Radarr | radarr.carr-family.org |
7878 |
| Prowlarr | prowlarr.carr-family.org |
9696 |
| Jellyseerr | seerr.carr-family.org |
5055 |
| Calibre-Web Automated | book.carr-family.org |
8083 |
| Audiobookshelf | audiobook.carr-family.org |
13378 |
| Audiobookrequest | abr.carr-family.org |
8000 |
| Shelfmark | shelfmark.carr-family.org |
8084 |
| OpenBooks | openbooks.carr-family.org |
8875 |
| Komga | komga.carr-family.org |
25600 |
| RomM | romm.carr-family.org |
8984 |
| FlareSolverr | (internal only) | 8191 |
Storage Layout
| Path | Contents |
|---|---|
/mnt/tank/media/content/movies |
Movies |
/mnt/tank/media/content/tv |
TV shows |
/mnt/tank/media/content/books |
Calibre library (managed by CWA — never move files manually) |
/mnt/tank/media/content/books-seeds |
MAM torrent seeding copies (never touch) |
/mnt/tank/media/content/audiobooks |
Audiobookshelf + Shelfmark audiobook downloads |
/mnt/tank/media/content/comics |
Komga library |
/mnt/tank/media/content/roms |
RomM ROM library (organize by platform subfolder) |
/mnt/tank/media/bookingest |
CWA ingest folder + Shelfmark ebook downloads |
Calibre-Web Automated (CWA)
Volumes: /mnt/tank/media/bookingest:/cwa-book-ingest, /mnt/tank/media/content/books:/calibre-library
Config: NETWORK_SHARE_MODE=true, CWA_WATCH_MODE=poll
Ingest mount gotcha — verify with docker inspect <container> | grep Mounts. If CWA sees an empty ingest folder after a compose update, the container wasn't redeployed. Fix:
pct exec 108 -- docker stack deploy -c /mnt/tank/appdata/portainer/compose/26/docker-compose.yml media-core
root:root ownership breaks deletes — CWA web server runs as abc (uid 1000) but the ingest processor runs as root, and NETWORK_SHARE_MODE=true skips the post-import chown. Files land as root:root and abc gets [Errno 13] Permission denied on delete. Fix existing library:
pct exec 102 -- chown -R 1000:1000 /mnt/tank/media/content/books
The MAM hardlink script runs a background chown 5 minutes after each torrent completes to keep future imports clean.
Bulk-delete always shows success (CWA bug) — /ajax/deleteselectedbooks always returns {"success": true}. Check container logs for Deleting book X failed if books reappear. config_calibre_split must be 0 in app.db — if 1 with wrong dir, all cover lookups silently fail.
Stale thumbnails: DELETE FROM thumbnail; in app.db to force regeneration. Cover generation runs nightly at 04:00.
Don't bulk-delete duplicates while ingest is running — drop files into bookingest, wait for CWA to finish all of them, then delete. Deleting mid-ingest causes silent EPERM failures.
Calibre Email Setup
- SMTP hostname:
192.168.2.83(Proton Bridge on PCT 109) - Port:
1025, Encryption: None - Login:
[email protected], Password:8A2SC9qao04GsSqBrfjtFg
calibredb Commands
# Get CWA container name pct exec 102 -- docker ps --format '{{.Names}}' | grep calibre # List all books as JSON pct exec 102 -- docker exec <cwa-container> calibredb list \ --fields=id,title,authors,series,series_index,tags,publisher,formats \ --sort-by=title -s '' --library-path=/calibre-library --for-machine # Remove by ID (deletes metadata + files; does NOT touch books-seeds) pct exec 102 -- docker exec <cwa-container> calibredb remove \ --library-path=/calibre-library <id1>,<id2>
Calibre Deduplication
Always keep the entry with the most metadata (series, series_index, tags, publisher). Never touch /mnt/tank/media/content/books-seeds.
Known duplicate patterns (exact-title matching misses these):
| Pattern | Example |
|---|---|
| Series prefix | "Mistborn 02 - The Well of Ascension" vs "The Well of Ascension" |
| Subtitle append | "After the Funeral: A Hercule Poirot Mystery" vs "After the Funeral" |
| Author punctuation | "J. K. Rowling" vs "J.K. Rowling" |
| Series tag in title | "Famine (The Four Horsemen Book 3)" vs "Famine" |
Calibre has a large Agatha Christie / Hercule Poirot set where every book exists twice: once as "Title" (bare) and once as "Title: A Hercule Poirot Mystery" (with series + series_index). Always keep the latter.
Calibre Library Cleanup
Junk patterns to watch for:
| Pattern | Search term | Notes |
|---|---|---|
| Comics — Spider-Man | spider-man |
Title starts with Amazing Spider-Man |
| Comics — Bone | bone + author Jeff Smith |
Individual chapter volumes |
| Lonely Planet (long) | lonely planet |
Author is Lonely Planet or Planet, Lonely & ... |
| Lonely Planet (short) | LP |
Title starts with LP , author Unknown |
| D&D — Forgotten Realms | forgotten realms |
Title starts with Forgotten Realms - |
| Rough Guides | rough guide |
Travel guides |
| Torrent metadata | anonamouse |
Titled Torrent_downloaded_from_anonamouse.net |
| RPG character sheets | character sheet |
e.g. Lone Wolf character sheets |
| Bare index files | index |
Title is exactly index |
Bulk-remove by keyword (filters precisely before deleting):
IDS=$(pct exec 102 -- docker exec <cwa-container> calibredb list \ --fields=id,title --sort-by=title -s '<keyword>' \ --library-path=/calibre-library --for-machine | python3 -c " import json,sys data=json.load(sys.stdin) ids=[str(b['id']) for b in data if '<keyword>'.lower() in b['title'].lower()] print(','.join(ids)) ") pct exec 102 -- docker exec <cwa-container> calibredb remove --library-path=/calibre-library ${IDS}
Shelfmark
Book search & request at shelfmark.carr-family.org. Image: ghcr.io/calibrain/shelfmark:latest (full, with browser automation). Config: /mnt/tank/appdata/shelfmark/plugins/.
After any config edit:
pct exec 108 -- docker service update --force media-core_shelfmark
Auth: mounts /mnt/tank/appdata/calibre-web-automated/app.db read-only to /auth/app.db — reuses Calibre-Web logins.
Volumes:
/mnt/tank/media/bookingest:/books— ebook download destination/mnt/tank/media/content/audiobooks:/audiobooks— audiobook download destination
qBittorrent connection (prowlarr_clients.json):
- URL:
192.168.2.190:8080, creds:admin / 32Ab0321!! - Ebook category:
books-shelfmark, Audiobook category:audiobooks DOWNLOAD_PROGRESS_UPDATE_INTERVAL=10(was 1 — caused login spam in qbt logs)HARDLINK_TORRENTS=false,HARDLINK_TORRENTS_AUDIOBOOK=false
Remote path mappings (advanced.json) — must use camelCase keys and host: "qbittorrent" (snake_case silently ignored):
[ {"host": "qbittorrent", "remotePath": "/data/bookingest", "localPath": "/books"}, {"host": "qbittorrent", "remotePath": "/data/content/audiobooks", "localPath": "/audiobooks"} ]
Komga
Comics & manga at komga.carr-family.org. Library: /mnt/tank/media/content/comics.
CONVERT_TO_CBZ=false, REPAIR_EXTENSIONS=false — do not re-enable (was renaming .cbr → .cbz, breaking seeding).
RomM (compose 32)
ROM manager with in-browser EmulatorJS at romm.carr-family.org. Port 8984, host-mode on 192.168.2.191. Auth: RomM built-in (no Authentik — would break EmulatorJS API calls).
- ROMs:
/mnt/tank/media/content/roms:/romm/library— organize by platform subfolder (e.g.roms/gba/,roms/n64/) - Config:
/mnt/tank/appdata/romm/config/config.yml— must exist before first start or settings won't persist - DB:
/mnt/tank/appdata/romm/db - Assets/covers:
/mnt/tank/appdata/romm/assets,/mnt/tank/appdata/romm/resources - Metadata: IGDB client ID
u5audru2zn9na5a0x3wq26yslmk1s5, RetroAchievements keyUV9Xf6VdH9D8N1bpzuePqAAUkwGErZW5
pct exec 108 -- docker stack deploy -c /mnt/tank/appdata/portainer/compose/32/docker-compose.yml romm
Jellyfin (PCT 101 — standalone, not Swarm)
Media server at jellyfin.carr-family.org. Static route in Traefik → 192.168.2.191:8096.
Audio Recorder (PCT 107 — stack: audiorecorder)
Browser-based system audio capture at audiorecorder.carr-family.org. Chrome/Edge only — enable "Share system audio" in the share dialog. Transcription via Whisper on PCT 109.
- Image:
audiorecorder:latest— built locally on PCT 107 from/tank/appdata/audiorecorder/app/ - Recordings:
/tank/appdata/audiorecorder/recordings/—.webmfiles with.txttranscripts alongside - Compose:
/tank/appdata/audiorecorder/docker-compose.yml(must be piped to PCT 108 for deploy)
Rebuild & redeploy:
tar -cf - -C /tank/appdata/audiorecorder/app . | pct exec 107 -- bash -c 'mkdir -p /tmp/audiorecorder-build && tar -xf - -C /tmp/audiorecorder-build' pct exec 107 -- docker build -t audiorecorder:latest /tmp/audiorecorder-build/ pct exec 108 -- docker service update --force audiorecorder_audiorecorder
FlareSolverr
Cloudflare bypass for Prowlarr indexers. Internal only at 192.168.2.191:8191.
